/* Copyright (C) 2003, Charles Wang.
 * Author:  Charles Wang <charles@linux.net.cn>
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANT; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public LIcense for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, 59 Temple
 * Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#ifndef  TEST
#define  TEST
#endif
#include "testshow.h"

static void fdprintf(int fd, const char *fmt, ...) {
    char  buffer[256];
    va_list  ap;
    va_start(ap, fmt);
    vsnprintf(buffer, sizeof(buffer), fmt, ap);
    write(fd, buffer, strlen(buffer));
    va_end(ap);
}

static void recur_showxml(outfunc of, int fd, int lvl, const mxml_node_t *mxn) {
    int  i;
    outfunc  ofunc = of ? of : fdprintf;
    while(mxn != NULL) {
	for (i = 0; i < lvl; ++i)
	    ofunc(fd, "    ");
	switch(mxn->type) {
	case MXML_ELEMENT:
	    ofunc(fd, "MXML_ELEMENT:");
	    ofunc(fd, "%s(", mxn->value.element.name);
	    for (i = 0; i < mxn->value.element.num_attrs; ++i)
		ofunc(fd, "%s=\"%s\" ",
		      mxn->value.element.attrs[i].name,
		      mxn->value.element.attrs[i].value);
	    ofunc(fd, ")\n");
	    break;
	case MXML_INTEGER:
	    ofunc(fd, "MXML_INTEGER: [%d]\n", mxn->value.integer);
	    break;
	case MXML_OPAQUE:
	    ofunc(fd, "MXML_OPAQUE: [%s]\n", mxn->value.opaque);
	    break;
	case MXML_REAL:
	    ofunc(fd, "MXML_REAL: [%f]\n", mxn->value.real);
	    break;
	case MXML_TEXT:
	    ofunc(fd, "MXML_TEXT: [%s]\n", mxn->value.text.string);
	    break;
	default:
            printf("UNKNOWN:\n");
	    break;
	}
	recur_showxml(of, fd, lvl + 1, mxn->child);
	mxn = mxn->next;
    }
}
void showxml(outfunc of, int fd, const mxml_node_t *mxn) {
    recur_showxml(of, fd, 0, mxn);
}

void showtext(outfunc of, int fd, const char *filename) {
    int      ifd;
    char     buffer[128];
    int      rn;
    outfunc  ofunc = of ? of : fdprintf;
    if ((ifd = open(filename, O_RDONLY)) < 0) {
	ofunc(fd, "Can't open %s\n", filename);
	return;
    }
    while ((rn = read(ifd, buffer, sizeof(buffer) - 1)) > 0) {
	buffer[rn] = 0;
	ofunc(fd, "%s", buffer);
    }
    close(ifd);
}

void showbin(outfunc of, int fd, const char *filename, int limit) {
    int           ifd;
    unsigned char buffer[16];
    unsigned      address;
    int           i, rn;
    outfunc       ofunc = of ? of : fdprintf;
    if ((ifd = open(filename, O_RDONLY)) < 0) {
	ofunc(fd, "Can't open %s\n", filename);
	return;
    }
    address = 0;
    while ((rn = read(ifd, buffer, sizeof(buffer))) > 0) {
	ofunc(fd, "%08x|", address);
	for (i = 0; i < sizeof(buffer); ++i)
	    if (i < rn)
		ofunc(fd, "%02x%c", buffer[i], (i < rn - 1) ? ' ' : '|');
	    else
		ofunc(fd, "  %c", (i < rn - 1) ? ' ' : '|');
	for (i = 0; i < sizeof(buffer); ++i)
	    ofunc(fd, "%c", (i >= rn) ? ' ':
		     (buffer[i] >= ' ' && buffer[i] < 127) ? buffer[i] : '.');
	ofunc(fd, "|\n");
	address += rn;
	if (limit > 0 && address > limit)  break;
    }
    close(ifd);
}

void showfile(outfunc of, int fd, const char *filename) {
    struct stat st;
    outfunc     ofunc = of ? of : fdprintf;
    if (lstat(filename, &st)) {
	ofunc(fd, "Can't stat %s\n", filename);
	return;
    }
    ofunc(fd, "%c",
	     S_ISLNK(st.st_mode) ? 'l':
	     S_ISSOCK(st.st_mode) ? 's':
	     S_ISFIFO(st.st_mode) ? 'f':
	     S_ISBLK(st.st_mode) ? 'b':
	     S_ISCHR(st.st_mode) ? 'c':
	     S_ISDIR(st.st_mode) ? 'd': '-');
    ofunc(fd, "%c%c%c%c%c%c%c%c%c:%s%s%s%4u%6u.%6u",
	     (st.st_mode & S_IRUSR) ? 'r' : '-',
	     (st.st_mode & S_IWUSR) ? 'w' : '-',
	     (st.st_mode & S_IXUSR) ? 'x' : '-',
	     (st.st_mode & S_IRGRP) ? 'r' : '-',
	     (st.st_mode & S_IWGRP) ? 'w' : '-',
	     (st.st_mode & S_IXGRP) ? 'x' : '-',
	     (st.st_mode & S_IROTH) ? 'r' : '-',
	     (st.st_mode & S_IWOTH) ? 'w' : '-',
	     (st.st_mode & S_IXOTH) ? 'x' : '-',
	     (st.st_mode & S_ISUID) ? "su" : "--",
	     (st.st_mode & S_ISGID) ? "sg" : "--",
	     (st.st_mode & S_ISVTX) ? "st" : "--",
	     st.st_nlink,
	     st.st_uid, st.st_gid);
    if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
	ofunc(fd, "   %3u,%3u", major(st.st_rdev), minor(st.st_rdev));
    else
	ofunc(fd, "%10lu", st.st_size);
    ofunc(fd, " %s\n", filename);
}

void showdir(outfunc of, int fd, const char *filename) {
    DIR *d;  struct dirent *de;
    char     fnbuf[256];
    outfunc  ofunc = of ? of : fdprintf;
    if ((d = opendir(filename)) == NULL) {
	ofunc(fd, "Can't opendir %s\n", filename);
	return;
    }
    while ((de = readdir(d)) != NULL) {
	snprintf(fnbuf, sizeof(fnbuf), "%s/%s", filename, de->d_name);
	showfile(of, fd, fnbuf);
    }
    closedir(d);
}

void showtree(outfunc of, int fd, const char *filename) {
    DIR  *d;  struct dirent *de;
    char  fnbuf[256];
    struct stat st;
    showdir(of, fd, filename);
    if ((d = opendir(filename)) == NULL) return;
    while ((de = readdir(d)) != NULL) {
	if (!strcmp(de->d_name, "."))  continue;
	if (!strcmp(de->d_name, ".."))  continue;
	snprintf(fnbuf, sizeof(fnbuf), "%s/%s", filename, de->d_name);
	if (lstat(fnbuf, &st))  continue;
	if (!S_ISDIR(st.st_mode))  continue;
	showtree(of, fd, fnbuf);
    }
    closedir(d);
}
